﻿// HSS.Interlink.Web.BaseHandler.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.html)
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       BaseHandler.cs
// Author:     HSS\gbanta
// Created:    07/16/2011
// Modified:   07/22/2011
// ----------------------------------------------------------------------------
namespace HSS.Interlink.Web
{
	#region Using Directives
	using System;
	using System.IO;
	using System.Reflection;
	using System.Security.Permissions;
	using System.Web;
	using System.Web.Hosting;
	using Microsoft.Win32;
	#endregion

	#region BaseHandler
	/// <summary>
	/// BaseHandler is the base implementation of a File download and upload
	/// handlers and handles the Protocol portion of a request.
	/// </summary>
	public abstract class BaseHandler
	{
		#region Constructors
		/// <summary>
		/// Constructor
		/// </summary>
		public BaseHandler()
		{
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets the current HttpContext
		/// </summary>
		public HttpContext Context
		{
			get;
			private set;
		}
		/// <summary>
		/// Gets the current user name if the context user identity is available; otherwise an empty string.
		/// </summary>
		public string UserName
		{
			get
			{
				if (null != this.Context && null != this.Context.User && null != this.Context.User.Identity)
					return this.Context.User.Identity.Name;
				else
					return string.Empty;
			}
		}
		/// <summary>
		/// Gets any Metadata associated with the current request
		/// </summary>
		public string Metadata
		{
			get;
			internal set;
		}
		/// <summary>
		/// The query string parsed into it their individual properties
		/// </summary>
		public FileQuery Query
		{
			get;
			private set;
		}
		/// <summary>
		/// Gets the current file Name.
		/// </summary>
		public string FileName { get { return this.Query.FileName; } }
		/// <summary>
		/// Gets or sets the number of minutes that files contained in the 
		/// Upload/Download TempFolder are purged (Default: 5 minutes).
		/// <para>
		/// Set to zero to turn off purging. Cannot be less than 5 minutes 
		/// or more than 660 minutes and is only triggered/run when a new
		/// request is received, as to avoid using a timer.
		/// </para>
		/// </summary>
		public int PurgeInterval
		{
			get { return _purgeInterval; }
			set
			{
				if ((value != 0 && (value < 5 || value > 660)))
					throw new ArgumentNullException("PurgeInterval");
				_purgeInterval = value;
			}
		} int _purgeInterval = 5;
		#endregion

		#region Methods
		/// <summary>
		/// Gets the file Content-Type Description based on the file name and extension from the Windows Registry.
		/// </summary>
		/// <param name="fileName">The name of the file to get the Description for. (Example: file.ext)</param>
		/// <returns>The file description if found; otherwise a plain string containing 'User File'</returns>
		/// <exception cref="System.Security.SecurityException">A caller higher in the call stack 
		/// does not have the permission specified by the current instance.-or- A caller higher in the call stack has called 
		/// System.Security.CodeAccessPermission.Deny() on the current permission object.</exception>
		public virtual string GetContentTypeDescription(string fileName)
		{
			return InterlinkUtilities.GetContentTypeDescription(fileName);
		}
		/// <summary>
		/// Gets a folder relative to the ApplicationPhysicalPath.
		/// </summary>
		/// <param name="folderName">The name of the folder</param>
		/// <returns>The folder's full path</returns>
		public virtual string GetFolder(string folderName)
		{
			return InterlinkUtilities.GetFolder(folderName);
		}

		#region Abstract
		/// <summary>
		/// Validate the current request.
		/// </summary>
		/// <returns>true if the request is allowed; otherwise false</returns>
		public abstract bool IsAuthorized();
		#endregion

		#region Protected
		/// <summary>
		/// This is a Helper method that sends a file back to the caller using 
		/// the TransmitFile method of the HttpResponse object.
		/// </summary>
		/// <param name="filePath">The full local path of the file to send.</param>
		/// <param name="fileContentLength">The size of the file (in bytes) being sent.</param>
		protected virtual void SendFile(string filePath, long fileContentLength)
		{
			this.SendFile(filePath, fileContentLength, MimeMapping.FromFileName(filePath));
		}
		/// <summary>
		/// This is a Helper method that sends a file back to the caller using 
		/// the TransmitFile method of the HttpResponse object.
		/// </summary>
		/// <param name="filePath">The full local path of the file to send.</param>
		/// <param name="fileContentLength">The size of the file (in bytes) being sent.</param>
		/// <param name="fileContentType">The HTTP MIME type of the the file being sent.</param>
		protected virtual void SendFile(string filePath, long fileContentLength, string fileContentType)
		{
			string fileName = Path.GetFileName(filePath);
			this.Context.Response.ContentType = fileContentType;
			this.Context.Response.AppendHeader("Content-Length", fileContentLength.ToString());
			this.Context.Response.AppendHeader("Content-Disposition", "inline; filename=" + fileName);
			this.Context.Response.ExpiresAbsolute = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0, 0));
			this.Context.Response.Clear();
			this.Context.Response.TransmitFile(filePath);
		}
		/// <summary>
		/// This is a Helper method that sends a file back to the caller using 
		/// the TransmitFile method of the HttpResponse object setting the 
		/// Content-Disposition to attachment.
		/// </summary>
		/// <param name="filePath">The full local path of the file to send.</param>
		/// <param name="fileContentLength">The size of the file (in bytes) being sent.</param>
		protected virtual void SendFileAsAttachment(string filePath, long fileContentLength)
		{
			this.SendFileAsAttachment(filePath, fileContentLength, MimeMapping.FromFileName(filePath));
		}
		/// <summary>
		/// This is a Helper method that sends a file back to the caller using 
		/// the TransmitFile method of the HttpResponse object setting the 
		/// Content-Disposition to attachment.
		/// </summary>
		/// <param name="filePath">The full local path of the file to send.</param>
		/// <param name="fileContentLength">The size of the file (in bytes) being sent.</param>
		/// <param name="fileContentType">The HTTP MIME type of the the file being sent.</param>
		protected virtual void SendFileAsAttachment(string filePath, long fileContentLength, string fileContentType)
		{
			string fileName = Path.GetFileName(filePath);
			this.Context.Response.ContentType = fileContentType;
			this.Context.Response.AppendHeader("Content-Length", fileContentLength.ToString());
			this.Context.Response.AppendHeader("Content-Disposition", "attachment; filename=" + fileName);
			this.Context.Response.ExpiresAbsolute = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0, 0));
			this.Context.Response.Clear();
			this.Context.Response.TransmitFile(filePath);
		}
		#endregion

		#region Internal
		internal bool CommonInit(HttpContext context, FileQuery fileQuery)
		{
			this.Context = context;
			this.Query = fileQuery;
			return true;
		}
		internal bool SetMetadata(string metaDataFile)
		{
			if (!File.Exists(metaDataFile))
			{
				this.Context.Response.Clear();
				this.Context.Response.StatusCode = 500;
				return false;
			}
			this.Metadata = File.ReadAllText(metaDataFile);
			File.SetLastAccessTime(metaDataFile, DateTime.Now);
			return true;
		}
		internal bool IsAuthorized(string metaDataFile)
		{
			if (!IsAuthorized())
			{
				if (!string.IsNullOrEmpty(metaDataFile) && File.Exists(metaDataFile))
					File.Delete(metaDataFile);
				this.Context.Response.Clear();
				this.Context.Response.StatusCode = 401;
				return false;
			}
			return true;
		}
		internal void PurgeTempFiles(string folder)
		{
			if (this.PurgeInterval == 0)
				return;
			foreach (var file in Directory.GetFiles(folder, "*.*", SearchOption.TopDirectoryOnly))
			{
				var fi = new FileInfo(file);
				var diff = DateTime.UtcNow.Subtract(fi.LastAccessTimeUtc).TotalMinutes;
				if (diff > this.PurgeInterval)
					fi.Delete();
			}
		}
		internal static bool CompareHash(byte[] hash1, byte[] hash2)
		{
			bool isEqual = false;

			if (hash1.Length == hash2.Length)
			{
				int i = 0;
				while ((i < hash1.Length) && (hash1[i] == hash2[i]))
				{
					i += 1;
				}
				if (i == hash1.Length)
				{
					isEqual = true;
				}
			}

			return isEqual;
		}
		#endregion

		#endregion
	}
	#endregion
}